package com.rcache.core.proxy;


import com.rcache.core.Element;
import com.rcache.core.cache.Rcache;
import com.rcache.core.distributed.IDistributedAgency;
import com.rcache.core.exception.DataPersistenceException;
import com.rcache.core.persistence.*;
import com.rcache.core.utils.EmailUtils;
import org.apache.log4j.Logger;

import javax.mail.MessagingException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.List;


/**
 * @desc 动态代理类
 * @author gzy
 * @time 2022-06-04
 */
public class JDKProxy implements InvocationHandler {

    Logger LOG=Logger.getLogger(Rcache.class);
    // 需要代理的目标对象

    private Rcache targetObject;

    /**
     * @desc 得到代理对象
     * @param targetObject
     * @return
     */
    public Object newProxy(Rcache targetObject) {
        // 将目标对象传入进行代理
        this.targetObject = targetObject;
        // 返回代理对象
        System.out.println(targetObject.getClass().getInterfaces());
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),new Class[]{Rcache.class},this);
    }





    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        baseListener(method, args);
        //分布式缓存操作
        if (targetObject.getAgency()!=null){
            distributedCache(targetObject,args,method);
        }

        // 调用invoke方法
        Object ret = null;
        ret = method.invoke(targetObject, args);

        //检测生命周期
        checkLifeCycle(targetObject);

        //持久化操作
        if (ret!=null&&targetObject.isDiskCache()){
            diskCacheCache(targetObject,ret,method);
        }
//        LOG.debug(cache.isWarning()+":"+cache.isWarningElementsResponse()+":"+cache.getWarningElements()+":"+cache.getSize());
        //如果开启了预警信息且未进行报警且达到预警阈值发送预警信息
        if (targetObject.isWarning()&&!targetObject.isWarningElementsResponse()&&targetObject.getWarningElements()<=targetObject.getSize()){
            sendWarningMsg(targetObject);
            targetObject.setWarningElementsResponse(true);
        }
        //监听命中
        hitListener(method,ret,targetObject);
        return ret;
    }

    /**
     * @desc 检测生命周期
     * @param cache
     */
    private void checkLifeCycle(Rcache cache) {
        LOG.debug("元素生命周期检测中……");
        //如果cache不是永恒的则进行监听是否有超过最大存活时间，或最大空闲时间的元素
        if (!cache.isEternal()){
            List<Element> elementList = cache.getValues();
            System.out.println(elementList);
            if (elementList.size()>0){
                for (Element element:elementList) {
//                    System.out.println(element);
//                    System.out.println(System.currentTimeMillis()-element.getLatestOfCreationAndUpdateTime()>cache.getMaxTimeToIdle());
//                    System.out.println(System.currentTimeMillis()-element.getCreationTime()>cache.getMaxTimeToIdle());
                    //如果大于最大空闲时间则移除该element
                    if (System.currentTimeMillis()-element.getLatestOfCreationAndUpdateTime()>cache.getMaxTimeToIdle()) {
                        cache.remove(element.getObjectKey());
                    }
                    //如果大于最大存活时间则移除该element
                    else if (System.currentTimeMillis()-element.getCreationTime()>cache.getMaxTimeToIdle()) {
                        cache.remove(element.getObjectKey());
                    }
                }
            }
        }
    }

    /**
     * @desc 发送预警信息
     */
    private void sendWarningMsg(Rcache cache) {
        LOG.debug("达到预警阈值发送预警信息……");
        //获取本机ip
        InetAddress inet4Address = null;
        try {
            inet4Address = Inet4Address.getLocalHost();
            String ipSelf = inet4Address.toString().split("/")[1];
            //发送告警信息
            EmailUtils.createMineMessage("缓存池超过预警阈值提醒","尊敬的管理员"+cache.getCacheName()+"缓存池已达到预警阈值，ip：为"+ipSelf,cache.getManagerEmail());
        } catch (UnknownHostException | UnsupportedEncodingException | MessagingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param method
     * @param ret
     * @param cache
     * @desc 缓存命中监听，播报监听是否命中、该cache的总命中率
     */
    private void hitListener(Method method, Object ret, Rcache cache) {
        if (method.getName().equals("get")){
            cache.increaseQueryHitCount();
            if (ret==null){
                LOG.debug("本次缓存未命中……");
            }else {
                LOG.debug("本次缓存命中了");
                ((Element)ret).updateLastAccessTime();
                //增加命中次数
                ((Element)ret).increaseHitCount();
                cache.increaseHitCount();
            }
//            System.out.println(cache.getHitCount());
//            System.out.println(cache.getQueryCount());
            float jl=Float.parseFloat(cache.getHitCount().toString())/Float.parseFloat(cache.getQueryCount().toString());
            if (cache.getHitCount()!=0){
                jl=jl==0? 1:jl;
            }

            LOG.debug("缓存命中率："+jl*100+"%");
        }
    }

    /**
     * @desc  最基本的监听器
     * @param method
     * @param args
     */
    private void baseListener(Method method, Object[] args) {
        if (args ==null){
            LOG.debug("执行："+ method.getName()+"方法");
        }else{
            LOG.debug("执行："+ method.getName()+"方法参数为："+ Arrays.toString(args));
        }
    }

    /**
     * @desc 持久化
     * @param cache
     * @param ret
     * @param method
     */
    private void diskCacheCache(Rcache cache, Object ret, Method method) {
        String diskCacheStrategy = cache.getDiskCacheStrategy();
        IDataPersistence dataPersistence=null;
        if (diskCacheStrategy.equals(EPersistence.TIME)){
            dataPersistence = DataTimePersistenceImpl.getInstance(cache);
        }else if (diskCacheStrategy.equals(EPersistence.SIZE)){
            dataPersistence = DateSizePersistenceImpl.getInstance(cache);
        }else {
            dataPersistence = DataPersistenceImpl.getInstance();
        }


        try {
            if (method.getName().equals("put")) {
                dataPersistence.put(cache,(Element) ret);
            } else if (method.getName().equals("replace")) {
                dataPersistence.replace(cache,(Element) ret);
            } else if (method.getName().equals("delete")) {
                dataPersistence.remove(cache,(Element) ret);
            } else if (method.getName().equals("get")) {
                dataPersistence.get(cache,(Element) ret);
            }
        } catch (DataPersistenceException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param cache
     * @param args
     * @param method
     * @desc 分布式缓存
     */
    private void distributedCache(Rcache cache, Object[] args, Method method) {
        IDistributedAgency distributedAgency=cache.getAgency();

        if (method.getName().equals("put")&&args.length==3&&(boolean)args[2]) {
            LOG.debug("新增分布式 添加 任务");
            distributedAgency.put(args);
        } else if (method.getName().equals("replace")&&args.length==3&&(boolean)args[2]) {
            LOG.debug("新增分布式 替换 任务");
            distributedAgency.replace(args);
        } else if (method.getName().equals("remove")&&args.length==2&&(boolean)args[1]) {
            LOG.debug("新增分布式 删除 任务");
            distributedAgency.delete(args);
        }
    }

}